package com.wonders;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;


/**
 * 1、实现了ObjectFactory的匿名的内部类不一定会获取一个代理bean，这块你要注意一下，仔细查看AbstractAutoProxyCreator类，这个类里有3处可能创建代理类的地方，第一处是在bean的实例化之前postProcessBeforeInstantiation，第二处也就是我们这一步（当前是第24步）执行时调用getReaylyBeanReference时会创建代理bean，第三处是在bean的初始化postProcessAfterInitialization中调用。
 * 如果被创建的bean自身是个代理bean，那ObjectFactory实例会在第24步就拿到代理的bean，绑定到自己身上，但如果被创建的bean自身不是一个代理类，它的子类是一个代理类，这种情况很常见，比如你的Service接口是个bean，通常你会在子实现中使用一些AOP的注解例如@Async，那么这种情况就是子类是个代理bean（使用JDK代理），它会使用第三处也就是在bean初始化完成的后置处理器中完成（还有一种是基类自身未实现任何接口但是内部使用了AOP注解例如@Async，这种使用的是CGLIB代理，也是在第三处的bean初始化完成后的后置处理器中完成），工作中很多人认为代理的场景是之后再bean初始化之后才会创建，其实不然。
 * 2、spring解决循环依赖的核心原理是：当beanX被循环依赖时，这时一级缓存中还没有beanX，就会通过beanX对应的三级缓存Map<String,ObjectFactory> singletonFactorys来获取bean的早期引用（获取bean的早期引用代码也就是第24步的代码）。在这段代码中可以看出通过三级缓存获取早期引用时，如果当前容器中有SmartInstantiationAwareBeanPostProcessor，那么就通过SmartInstantiationAwareBeanPostProcessor来获取bean的早期引用。SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference仅被AbstractAutoProxyCreator#getEarlyBeanReference实现了。可以看出它会调用 AbstractAutoProxyCreator#wrapIfNecessary()，也就是 Spring 创建 AOP 代理类时调用的方法。
 * 也就是说，getEarlyBeanReference() 会提前调用 AbstractAutoProxyCreator#wrapIfNecessary() 来生成 AOP 代理类。
 * 这样的话，当普通的 AOP 代理 bean 被循环依赖时，就能被正确的注入属性引用。普通的 AOP 代理 bean: 指通过 AbstractAutoProxyCreator#wrapIfNecessary() 产生 AOP 代理的 bean。其中包括：
 * ①. 自定义的 @Aspect 产生 AOP 代理的 bean
 * ②. @Transactional 产生的 AOP 代理 bean
 * 3、非SmartInstantiationAwareBeanPostProcessor生产代理的情况如下，Spring产生AOP代理是在bean的初始化之后，通过BeanPostProcessor#postProcessorAfterInitialization来产生AOP代理类的。在Spring中其实是有两种方式产生AOP代理类的：①通过AbstractAdvisorAutoProxyCreator-》即：AbstractAdvisorAutoProxyCreator#wrapIfNecessary()。②通过AbstractAdvisingBeanPostProcessor。也就是说，Spring除了使用AbstractAutoProxyCreator来产生AOP代理之外，还可以通过AbstractAdvisingBeanPostProcessor来产生AOP代理bean。而AbstractAdvisingBeanPostProcessor是没有实现SmartInstantizationAwareBeanPostProcessor的，通过 AbstractAdvisingBeanPostProcessor 产生的 AOP 代理 bean 被循环依赖时，通过三级缓存 Map<String, ObjectFactory<?>> singletonFactories 来获取 bean 的早期引用时，就不会提前创建 AOP 代理 bean，也就是拿不到最终暴露到 Spring 容器中的 AOP 代理 bean 的早期引用，这样就会导致这种 AOP 代理 bean 循环依赖注入时的引用不正确。
 * 这种情况是不被允许的，Spring 在 initializeBean 之后，做了 check（在AbstractBeanFactory#doGetBean中第一个if判断里），检验二级缓存中的 bean 与最终暴露到 Spring 容器中的 bean 是否是相同的，如果不同，就会报错。
 * 4、真实测试，如果两个bean的某个方法都使用了@Async注解标注，则spring在创建bean时将会抛出循环依赖异常，当两个bean都被@Async标注时，不管哪个bean使用@Lazy注解或者两个都使用没没有任何效果。如果两个bean的某个方法都使用了@Transactional标注，则创建循环依赖的bean将会在早期阶段就创建而不是在bean初始化之后创建。
 *
 * @author：wonders
 * @date： 2022/7/18  10:26
 */
@SpringBootApplication
@EnableAsync
@EnableTransactionManagement
public class CircularDependencyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(CircularDependencyApplication.class);
//        run.getBean("e"); // @Lazy时必须手动调用才会创建
//        run.getBean("f");
//        run.close();
    }
}
